use std::io;
use std::fs;
use std::io::Write;

use crate::Position;
use crate::Row;

#[derive(Default)]
pub struct Document {
    rows: Vec<Row>,
    pub file_name: Option<String>,
    dirty: bool,    //will be set to true if any text changes
}

impl Document {
    pub fn open(file_name: &str) -> Result<Document, io::Error> {
        let r = fs::read_to_string(file_name)?;
        let mut rows = Vec::new();
        for s in r.lines() {
            rows.push(Row::from(s));
        }
        Ok(Self {
            rows,
            file_name: Some(String::from(file_name)),
            dirty: false,
        })
    }

    pub fn save(&mut self) -> Result<(), io::Error> {
        if let Some(file_name) = &self.file_name {
            let mut f = fs::File::create(file_name)?;
            for row in &self.rows {
                f.write_all(row.as_bytes())?;
                f.write_all(b"\n")?;
            }
            self.dirty = false;
        }
        
        Ok(())
    }

    pub fn find(&self, query: &str) -> Option<Position> {
        for (y, row) in self.rows.iter().enumerate() {
            if let Some(x) = row.find(query) {
                return Some(Position {x, y});
            }
        }

        None
    }

    pub fn save_as(&self, _path: &str) -> Result<(), io::Error>{
        todo!()
    }

    pub fn row(&self, index: usize) -> Option<&Row> {
        self.rows.get(index)
    }

    pub fn is_empty(&self) -> bool {
        self.rows.is_empty()
    }

    pub fn len(&self) -> usize {
        self.rows.len()
    }

    pub fn len_of_row(&self, index: usize) -> usize {
        if let Some(r) = self.row(index) {
            r.len()
        }else {
            0
        }
    }

    pub fn insert(&mut self, at: &Position, c: char) {
        if at.y > self.len() {
            return;
        }

        self.dirty = true;
        if c == '\n' {
            self.insert_new_line(at);
            return;
        }
        
        if at.y == self.len() {     //insert into a new row
            let mut row = Row::default();
            row.insert(0, c);
            self.rows.push(row);
        // }else if at.y < self.len() {
        }else {
            self.rows[at.y].insert(at.x, c);
        }
    }

    pub fn delete(&mut self, at: &Position) {
        if at.y >= self.len() {
            return;
        }

        self.dirty = true;
        if at.x == self.rows[at.y].len() && at.y < self.len().saturating_sub(1) {   //merge this row wich next row
            let next_row = self.rows.remove(at.y + 1);
            self.rows[at.y].append(&next_row);
        }
        else {
            self.rows[at.y].delete(at.x);
        }

    }

    fn insert_new_line(&mut self, at: &Position) {
        //push a row if document is empty
        if self.len() == at.y {
            self.rows.push(Row::default());
        }
        let remainder = self.rows[at.y].split(at.x);
        self.rows.insert(at.y + 1, remainder);
    }

    pub fn is_dirty(&self) -> bool {
        self.dirty
    }

}